package org.cagt.probability;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

/**
 * Iterator for federation of resetable iterators. Federation is to be
 * understand as list of iterators used to browse the same type of elements.
 * Iterator browsed through all possible combinations of elements.of iterators
 * contained within.
 * 
 * @author konrad.szalkowski (at) gmail.com
 * 
 * @param <T>
 *            Type of elements used inside the iterators.
 */
public class FederatedResetIterators<T> implements ResetIterator<List<T>> {

	private List<ResetIterator<T>> iterators;
	private List<T> currentResults;

	/**
	 * Default constructor requiring list of resetable iterators.
	 * 
	 * @param its
	 *            List of iterators. Not null or empty, also after reset all
	 *            iterators inside must be able to move.
	 */
	public FederatedResetIterators(List<? extends ResetIterator<T>> its) {
		// Start terms
		if (its == null) {
			throw new NullPointerException();
		}
		if (its.isEmpty()) {
			throw new IllegalArgumentException("Iterator list is empty.");
		}
		// Set iterator table copy.
		iterators = new ArrayList<ResetIterator<T>>(its);
		currentResults = new ArrayList<T>();
		// Reset
		reset();
		// Check if all can move after reset.
		boolean allCanMove = true;
		for (ResetIterator<T> iterator : iterators) {
			allCanMove = allCanMove && iterator.hasNext();
		}
		if (!allCanMove) {
			throw new IllegalArgumentException("Iterators cannot be stalled.");
		}
	}

	public void reset() {
		for (ResetIterator<T> iterator : iterators) {
			iterator.reset();
		}
		currentResults.clear();
	}

	public boolean hasNext() {
		boolean canMove = false;
		for (ResetIterator<T> iterator : iterators) {
			canMove = canMove || iterator.hasNext();
		}
		return canMove;
	}

	public List<T> next() {
		ListIterator<ResetIterator<T>> iteratorOfIterators = iterators
				.listIterator();
		while (iteratorOfIterators.hasNext()) {
			// Get current iterator
			ResetIterator<T> iterator = iteratorOfIterators.next();
			if (iterator.hasNext()) {
				// Starting state
				if (currentResults.size() < iterators.size()) {
					// Add till full
					currentResults.add(iterator.next());
				} else {
					// Exchange current position result
					currentResults.set(iteratorOfIterators.previousIndex(),
							iterator.next());
					// Back off
					iteratorOfIterators.previous();
					// Reset all previous
					while (iteratorOfIterators.hasPrevious()) {
						ResetIterator<T> previous = iteratorOfIterators
								.previous();
						previous.reset();
						currentResults.set(iteratorOfIterators.nextIndex(),
								previous.next());
					}
					break;
				}
			}
		}
		// Stop
		return new ArrayList<T>(currentResults);
	}

}
